package engine.util.math;

import java.nio.FloatBuffer;

import org.lwjgl.BufferUtils;
import org.lwjgl.util.vector.Matrix4f;
import org.lwjgl.util.vector.Vector3f;

public class Matrix extends Matrix4f {

	/**
	 * 
	 */
	private static final long serialVersionUID = -5708140434641481992L;
	
	// the floatbuffer which is used to create the opengl matrix
	private FloatBuffer floatBuffer;

	public Matrix() {
		super();

		floatBuffer = BufferUtils.createFloatBuffer(16);
	}

	public Matrix(Matrix src) {
		this();
		load(src);
	}

	/**
	 * Returns this matrix ready for use with lwjgl.
	 * 
	 * @return
	 */
	public FloatBuffer toFloatBuffer() {
		floatBuffer.rewind();
		store(floatBuffer);
		floatBuffer.rewind();

		return floatBuffer;
	}

	public void perspective(float fov, float ratio, float zNear, float zFar) {
		setZero();

		float yScale = 1.0f / (float) Math.tan(Math.toRadians(fov / 2.0f));
		float xScale = yScale / ratio;
		float frustumLength = zFar - zNear;

		m00 = xScale;
		m11 = yScale;
		m22 = -((zFar + zNear) / frustumLength);
		m23 = -1;
		m32 = -((2 * zNear * zFar) / frustumLength);
	}

	public void ortho(float left, float right, float bottom, float top,
			float zNear, float zFar) {
		setZero();

		float rl = right - left;
		float tb = top - bottom;
		float fn = zFar - zNear;

		m00 = 2 / rl;
		m30 = -((right + left) / rl);
		m11 = 2 / tb;
		m31 = -((top + bottom) / tb);
		m22 = -2 / fn;
		m32 = -((zFar + zNear) / fn);
		m33 = 1;
	}

	public void lookAt(float eyeX, float eyeY, float eyeZ, float lookAtX,
			float lookAtY, float lookAtZ, float upX, float upY, float upZ) {
		setIdentity();

		Vector3f forward = new Vector3f(lookAtX - eyeX, lookAtY - eyeY, lookAtZ
				- eyeZ);
		Vector3f up = new Vector3f(upX, upY, upZ);
		Vector3f side = new Vector3f();
		forward.normalise();

		Vector3f.cross(forward, up, side);
		side.normalise();

		Vector3f.cross(side, forward, up);
		up.normalise();

		m00 = side.getX();
		m10 = side.getY();
		m20 = side.getZ();

		m01 = up.getX();
		m11 = up.getY();
		m21 = up.getZ();

		m02 = -forward.getX();
		m12 = -forward.getY();
		m22 = -forward.getZ();

		translate(new Vector3f(-eyeX, -eyeY, -eyeZ));
	}
	
	private static FloatBuffer identity = BufferUtils.createFloatBuffer(16).put(new float[]{1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1});
	private static Matrix normalMatrix = new Matrix();
	private static FloatBuffer staticBuffer = BufferUtils.createFloatBuffer(9);
	
	public static FloatBuffer identity() {
		identity.rewind();
		
		return identity;
	}
	
	public static FloatBuffer getNormalMatrix(Matrix viewMatrix, Matrix modelMatrix) {
		Matrix.mul(viewMatrix, modelMatrix, normalMatrix);
		normalMatrix.invert();
		normalMatrix.transpose();
		
		staticBuffer.rewind();
		normalMatrix.store3f(staticBuffer);

		return staticBuffer;
	}
}
